﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Minestorm.Evolved.IoC
{
	public class TypeCreator
	{
		private ObjectFactory _objectFactory;

		public TypeCreator(ObjectFactory objectFactory)
		{
			_objectFactory = objectFactory;
		}

		public Object CreateInstanceFromInterfaceType(Type interfaceType)
		{
			ConcreteType concreteType = _objectFactory.GetConcreteTypeFromInterfaceType(interfaceType);

			if (concreteType == null)
				return null;

			if (concreteType.Singleton)
				return _objectFactory.GetSingletonInstance(interfaceType);

			return CreateInstanceFromConcreteType(concreteType);
		}

		public Object CreateInstanceFromConcreteType(ConcreteType concreteType)
		{
			Type[] parameterTypes = GetBestParameterTypesFromConstructionMap(concreteType.ConstructionMap);

			if (parameterTypes == null)
				return CreateInstanceFromConcreteTypeWithNullArguments(concreteType);

			List<Object> parameterInstances = new List<Object>();

			foreach (Type interfaceType in parameterTypes)
				parameterInstances.Add(CreateInstanceFromInterfaceType(interfaceType));

			return Activator.CreateInstance(concreteType.Type, parameterInstances.ToArray());
		}

		private Object CreateInstanceFromConcreteTypeWithNullArguments(ConcreteType concreteType)
		{
			Type[] bestParameters = null;

			foreach (Type[] types in concreteType.ConstructionMap.Types)
				if (bestParameters == null || types.Length < bestParameters.Length)
					bestParameters = types;

			List<Object> nullParameters = new List<Object>();

			for (Int32 i = 0; i < bestParameters.Length; i++)
				nullParameters.Add(null);

			return Activator.CreateInstance(concreteType.Type, nullParameters.ToArray());
		}

		public Boolean CanCreateParametersFromTypeArray(Type[] parameterTypes)
		{
			foreach (Type interfaceType in parameterTypes)
				if (!_objectFactory.TypeMapContainsInterfaceType(interfaceType))
					return false;

			return true;
		}

		public Type[] GetBestParameterTypesFromConstructionMap(ConstructionMap constructionMap)
		{
			Type[] types = null;

			foreach (Type[] parameterTypes in constructionMap.Types)
				if (CanCreateParametersFromTypeArray(parameterTypes))
					return parameterTypes;

			return types;
		}
	}
}
